package com.mingyuan.library.config.prometheus;


import com.mingyuan.library.config.prometheus.metrics.GaugeMetrics;
import com.mingyuan.library.config.prometheus.metrics.TomcatMetrics;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class QMonitor {
    private static final Logger logger = LoggerFactory.getLogger(QMonitor.class);
    private static String APPLICATION_NAME = null;
    private static PrometheusMeterRegistry registry = null;
    private static final Map<String, Counter> counterMap = new ConcurrentHashMap(1000);
    private static final Map<String, Timer> timerMap = new ConcurrentHashMap(1000);
    private static final Map<String, Gauge> gaugeMap = new ConcurrentHashMap(1000);
    public static final String DUBBO_REQUESTS_METHOD_EXCEPTION = "dubbo_requests_method_exception";
    public static final String DUBBO_REQUESTS_METHOD_TIMER = "dubbo_requests_method_timer";
    public static final String WEB_REQUESTS_METHOD_EXCEPTION = "web_requests_method_exception";
    public static final String WEB_REQUESTS_METHOD_TIMER = "web_requests_method_timer";
    private static final String CUSTOMIZE_METHOD = "customize_method";
    private static final Object OBJC = new Object();
    private static final Object OBJT = new Object();
    private static final Object GAUGE_OBJECT = new Object();

    public QMonitor() {
    }

    public static void init() {
        List<Tag> tags = new ArrayList();
        String application = System.getProperty("application");
        if (StringUtils.isNotBlank(application)) {
            tags.add(new ImmutableTag("application", application));
            APPLICATION_NAME = application;
        }

        registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
        (new JvmGcMetrics(tags)).bindTo(registry);
        (new JvmMemoryMetrics(tags)).bindTo(registry);
        (new JvmThreadMetrics(tags)).bindTo(registry);
        (new ClassLoaderMetrics(tags)).bindTo(registry);
        (new UptimeMetrics(tags)).bindTo(registry);
        (new ProcessorMetrics(tags)).bindTo(registry);
        (new FileDescriptorMetrics(tags)).bindTo(registry);
        TomcatMetrics.monitor(registry, tags);
    }

    public static void init(PrometheusMeterRegistry prometheusMeterRegistry) {
        registry = prometheusMeterRegistry;
    }

    public static PrometheusMeterRegistry register() {
        if (null == registry) {
            init();
        }

        return registry;
    }

    public static void record(String methodName) {
        recordOne("customize_method", "method", methodName);
    }

    public static void time(String methodName, long time) {
        recordTime("customize_method", time, "method", methodName);
    }

    public static void time(String methodName, long time, TimeUnit timeUnit) {
        recordTimeWithTags("customize_method", time, new String[]{"method", methodName}, timeUnit);
    }

    public static void recordOne(String key) {
        recordManyWithTags(key, 1, (String[])null);
    }

    public static void recordOne(String key, String tagKey, String tagValue) {
        recordManyWithTags(key, 1, new String[]{tagKey, tagValue});
    }

    public static void recordMany(String key, int value, Map<String, String> tagMap) {
        recordManyWithTags(key, value, getTags(tagMap));
    }

    public static void recordManyWithTags(String key, int value, String[] tags) {
        if (registry != null) {
            if (value <= 0) {
                logger.warn("Record count must not be negative. key={}, value={}", key, value);
            } else {
                try {
                    getCounter(key, tags).increment((double)value);
                } catch (Exception var4) {
                    logger.warn("record[{}] {}", key, var4.getMessage());
                }

            }
        }
    }

    public static void recordTime(String key, long mills) {
        recordTimeWithTags(key, mills, (String[])null, TimeUnit.MILLISECONDS);
    }

    public static void recordTime(String key, long mills, String tagKey, String tagValue) {
        recordTimeWithTags(key, mills, new String[]{tagKey, tagValue}, TimeUnit.MILLISECONDS);
    }

    public static void recordTime(String key, long mills, Map<String, String> tagMap) {
        recordTimeWithTags(key, mills, getTags(tagMap), TimeUnit.MILLISECONDS);
    }

    public static void recordTimeWithTags(String key, long time, String[] tags, TimeUnit timeUnit) {
        if (registry != null) {
            if (time < 0L) {
                logger.warn("Record time must not be negative. key={}, time={}", key, time);
            } else {
                try {
                    getTimer(key, tags).record(time, timeUnit);
                } catch (Exception var6) {
                    logger.warn("record[{}] {}", key, var6.getMessage());
                }

            }
        }
    }

    private static Counter getCounter(String metricsName, String... tags) {
        String entryKey = getEntryKey(metricsName, tags);
        Counter counter = (Counter)counterMap.get(entryKey);
        if (counter == null) {
            synchronized(OBJC) {
                counter = (Counter)counterMap.get(entryKey);
                if (counter != null) {
                    return counter;
                }

                if (StringUtils.isNotBlank(APPLICATION_NAME)) {
                    counter = Counter.builder(metricsName).tag("application", APPLICATION_NAME).tags(tags).register(registry);
                } else {
                    counter = Counter.builder(metricsName).tags(tags).register(registry);
                }

                counterMap.put(entryKey, counter);
            }
        }

        return counter;
    }

    private static Timer getTimer(String metricsName, String... tags) {
        String entryKey = getEntryKey(metricsName, tags);
        Timer timer = (Timer)timerMap.get(entryKey);
        if (timer == null) {
            synchronized(OBJT) {
                timer = (Timer)timerMap.get(entryKey);
                if (timer != null) {
                    return timer;
                }

                if (StringUtils.isNotBlank(APPLICATION_NAME)) {
                    timer = Timer.builder(metricsName).tag("application", APPLICATION_NAME).tags(tags).publishPercentiles(new double[]{0.95D}).register(registry);
                } else {
                    timer = Timer.builder(metricsName).tags(tags).publishPercentiles(new double[]{0.95D}).register(registry);
                }

                timerMap.put(entryKey, timer);
            }
        }

        return timer;
    }

    private static Gauge getGauge(String metricsName, GaugeMetrics gaugeMetrics, String... tags) {
        String entryKey = getEntryKey(metricsName, tags);
        Gauge gauge = (Gauge)gaugeMap.get(entryKey);
        if (gauge == null) {
            synchronized(GAUGE_OBJECT) {
                gauge = (Gauge)gaugeMap.get(entryKey);
                if (gauge != null) {
                    return gauge;
                }

                if (StringUtils.isNotBlank(APPLICATION_NAME)) {
                    gauge = Gauge.builder(metricsName, gaugeMetrics, GaugeMetrics::doubleValue).tag("application", APPLICATION_NAME).tags(tags).register(registry);
                } else {
                    gauge = Gauge.builder(metricsName, gaugeMetrics, GaugeMetrics::doubleValue).tags(tags).register(registry);
                }

                gaugeMap.put(entryKey, gauge);
            }
        }

        return gauge;
    }

    private static String getEntryKey(String metricsName, String... tags) {
        if (tags != null && tags.length != 0) {
            StringBuilder sb = new StringBuilder(metricsName);
            String[] var3 = tags;
            int var4 = tags.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String tag = var3[var5];
                sb.append(tag);
            }

            return sb.toString();
        } else {
            return metricsName;
        }
    }

    private static String[] getTags(Map<String, String> tagMap) {
        List<String> tags = new ArrayList();
        Iterator var2 = tagMap.entrySet().iterator();

        while(var2.hasNext()) {
            Entry<String, String> entry = (Entry)var2.next();
            tags.add(entry.getKey());
            tags.add(entry.getValue());
        }

        String[] tagArray = new String[tags.size()];
        return (String[])tags.toArray(tagArray);
    }
}